﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;

namespace Uploader
{
  /// <summary>
  /// Oggetto interno per upload ciascun elemento
  /// </summary>
  internal class Obj
  {
    public byte[] Buffer { get; set; }
    /// <summary>
    /// Numero totale di pacchetti in cui dividere l'upload
    /// </summary>
    public int Total { get; set; }
    /// <summary>
    /// Numero pacchetto corrente
    /// </summary>
    public int Current { get; set; }
    /// <summary>
    /// Numero di byte per ciascun pacchetto
    /// </summary>
    public int Chunk { get; set; }
    /// <summary>
    /// Nome del File
    /// </summary>
    public string ItemName { get; set; }
    /// <summary>
    /// Elemento grafico
    /// </summary>
    public UploaderItem UploaderItem { get; set; }

    public Obj()
    {
      Total = 10;
      Current = 1;
    }
  }

  /// <summary>
  /// Oggetto per trasporto informazioni personalizzate come stato della richiesta
  /// </summary>
  internal class CallState
  {
    public HttpWebRequest Request { get; set; }
    public Obj Item { get; set; }
  }

  public partial class MainPage : UserControl
  {
    //url risorsa che effettua effettiva scrittura sul server del file
    string remoteUploadPage = "UploadImage.aspx?total={0}&current={1}&filename={2}";

    List<Obj> oList = new List<Obj>();
    int nItems = 0;
    IEnumerable<FileInfo> Files { get; set; }
    public MainPage()
    {
      InitializeComponent();
      
      remoteUploadPage = String.Format("{0}{1}", Helper.GetBaseUri(), remoteUploadPage);

      this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
      if (Files != null)
      {
        Dispatcher.BeginInvoke(() =>
        {
          UploadingItemContainer.Children.Clear();
          oList.Clear();

          //recupero info sui file selezionati dall'utente
          foreach (FileInfo item in Files)
          {
            nItems++;
            UploaderItem uItem = new UploaderItem();

            Obj o = new Obj();

            using (System.IO.FileStream fileStream1 = item.OpenRead())
            {
              int streamLenght1 = (int)fileStream1.Length;
              o.Buffer = new byte[streamLenght1];
              o.Chunk = o.Buffer.Length / o.Total;
              fileStream1.Read(o.Buffer, 0, streamLenght1);
            }

            //nome del file da salvare sul server
            o.ItemName = String.Format("{0}{1}{2}", DateTime.Now.Ticks.ToString(), item.Name, item.Extension);

            uItem.ImageName.Text = item.Name;
            UploadingItemContainer.Children.Add(uItem);

            o.UploaderItem = uItem;
            oList.Add(o);
          }
        });
      }
    }

    public MainPage(IEnumerable<FileInfo> files)
      : this()
    {
      Files = files;
    }


    private void BrowsImageButton_Click(object sender, RoutedEventArgs e)
    {
      OpenFileDialog dialog = new OpenFileDialog();
      dialog.Multiselect = true;

      if (dialog.ShowDialog() == true)
      {
        Dispatcher.BeginInvoke(() =>
        {
          UploadingItemContainer.Children.Clear();
          oList.Clear();

          //recupero info sui file selezionati dall'utente
          foreach (FileInfo item in dialog.Files)
          {
            nItems++;
            UploaderItem uItem = new UploaderItem();

            Obj o = new Obj();

            using (System.IO.FileStream fileStream1 = item.OpenRead())
            {
              int streamLenght1 = (int)fileStream1.Length;
              o.Buffer = new byte[streamLenght1];
              o.Chunk = o.Buffer.Length / o.Total;
              fileStream1.Read(o.Buffer, 0, streamLenght1);
            }

            //nome del file da salvare sul server
            o.ItemName = String.Format("{0}{1}{2}", DateTime.Now.Ticks.ToString(), item.Name, item.Extension);

            uItem.ImageName.Text = item.Name;
            UploadingItemContainer.Children.Add(uItem);

            o.UploaderItem = uItem;
            oList.Add(o);
          }
        });
      }
    }

    private void SaveImageButton_Click(object sender, RoutedEventArgs e)
    {
      if (oList.Count > 0)
      {
        SaveImageButton.Content = "Caricamento in corso..";
        SaveImageButton.IsEnabled = false;
      }

      foreach (Obj o in oList)
      {
        UploadImage(o);
      }
    }

    private void UploadImage(Obj o)
    {
      Uri uri = new Uri(String.Format(
                  remoteUploadPage
              , o.Total
              , o.Current
              , o.ItemName));

      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
      request.Method = "POST";

      CallState state = new CallState() { Request = request, Item = o };

      //richiesta di apertura dello stream sul server
      request.BeginGetRequestStream(new AsyncCallback(RequestCallBack), state);
    }

    private void RequestCallBack(IAsyncResult asyncResult)
    {
      CallState state = asyncResult.AsyncState as CallState;
      Obj o = state.Item;
      HttpWebRequest request = state.Request;

      using (Stream postedStream = request.EndGetRequestStream(asyncResult))
      {
        int offset = (o.Current - 1) * o.Chunk;
        int count = o.Total == o.Current ? o.Buffer.Length / o.Total + o.Buffer.Length % o.Total : o.Buffer.Length / o.Total;
        //scrittura nello stream dei byte di ciascun pacchetto
        postedStream.Write(o.Buffer, offset, count);
      }
      request.BeginGetResponse(new AsyncCallback(ResponseCallBack), state);
    }

    private void ResponseCallBack(IAsyncResult asyncResult)
    {
      CallState state = asyncResult.AsyncState as CallState;
      Obj o = state.Item;
      HttpWebRequest request = state.Request;

      HttpWebResponse response = request.EndGetResponse(asyncResult) as HttpWebResponse;
      Stream responseStream = response.GetResponseStream();
      StreamReader reader = new StreamReader(responseStream);

      string result = reader.ReadToEnd();

      if (String.IsNullOrEmpty(result))
      {
        //aggiornamento dell'interfaccia utente per la progressione di upload
        Dispatcher.BeginInvoke(() => { o.UploaderItem.UploadingImage.Width = 30 * o.Current; });

        o.Current++;

        if (o.Current <= o.Total)
        {
          UploadImage(o);
        }
        else
        {
          Dispatcher.BeginInvoke(() =>
          {
            ResetUploadingImageBar(o);
          });
        }
      }
      else
      {
        Dispatcher.BeginInvoke(() =>
        {
          ResetUploadingImageBar(o);
          o.UploaderItem.ImageName.Text = "errore";
        });
      }
    }

    private void ResetUploadingImageBar(Obj o)
    {
      nItems--;

      oList.Remove(o);
      o.UploaderItem.ImageName.Text = string.Empty;

      if (nItems == 0)
      {
        UploadingItemContainer.Children.Clear();
        SaveImageButton.Content = "Salva";
        SaveImageButton.IsEnabled = true;
      }
    }
  }

  public static class Helper
  {
    /// <summary>
    /// Restituisce il path del sito host
    /// </summary>
    /// <returns></returns>
    public static string GetBaseUri()
    {
      string absoluteUriString = HtmlPage.Document.DocumentUri.AbsoluteUri;

      string localPath = HtmlPage.Document.DocumentUri.LocalPath.Replace("/", "");
      string path = absoluteUriString.Replace(localPath, "");
      if (!String.IsNullOrEmpty(HtmlPage.Document.DocumentUri.Query))
        path = path.Replace(HtmlPage.Document.DocumentUri.Query, "");

      path = path.Replace(HtmlPage.Document.DocumentUri.AbsolutePath, "");
      path = path.Replace(HtmlPage.Document.DocumentUri.Fragment, "");

      return path;
    }
  }
}
